//$Id$

package builders;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;

/**
 * This is the ATD for the following classes:
 * VincentAI
 * 
 * @author Vincent
 */
public class VincentAIATD {

	BuildingManager buildingManager;
	Building[] manager;
	TileManager tileManager;
	Tile[] tiles;
	File inputFile;
	GameState gameState;
	File expectedOutputFile;
	int testCount;
	int errorCount;

	/**
	 * This is the constructor of the VincentAIATD class.
	 * 
	 * @param inputFile				specify the file name of the file from which inputs are read
	 * @param expectedOutputFile	specify the file name of the file from which expected outputs are read
	 */
	VincentAIATD(String inputFile, String expectedOutputFile) {
		this.buildingManager = new BuildingManager("testDataForAIBuilding.txt");
		this.manager = buildingManager.getManager();
		this.tileManager = new TileManager("testDataForTile.txt");
		this.tiles = tileManager.getTiles();
		this.gameState = new GameState();
		this.gameState.setBuildingManager(this.buildingManager);
		this.gameState.setTileManager(this.tileManager);
		this.inputFile = new File(inputFile);
		this.expectedOutputFile = new File(expectedOutputFile);
	}

	/*
	 * this method will read each test cases from the file specified by inputFile and compare the results from the test cases against the expected outputs.
	 * the expected outputs are read from the file specified by expectedOutputFile.
	 * 
	 * the test will end when the line read from the input file is "end of test" .
	 * 
	 * this method will read the test line provided in the input file and match it to a method call that is from one of the three classes being tested.
	 */
	public void executeTestCases(){

		try {
			FileReader inputFileReader = new FileReader(inputFile);
			BufferedReader inputReader = new BufferedReader (inputFileReader);

			FileReader expectedOutputFileReader = new FileReader(expectedOutputFile);
			BufferedReader expectedOutputReader = new BufferedReader (expectedOutputFileReader);

			String line = inputReader.readLine();
			System.out.println(line);

			while (!(line = inputReader.readLine()).equals("end of test")){
				String testDescription = line;
				System.out.println(testDescription);

				String testLine = inputReader.readLine();

				int methodNumber = matchTestLineToMethodCall(testLine);

				switch (methodNumber) {
				case 1:
					checkSorting(inputReader, expectedOutputReader);
					break;
				case 2:
					checkSelectTileNormal(inputReader, expectedOutputReader);
					break;
				case 3:
					checkSelectTileHard(inputReader, expectedOutputReader);
					break;
				case 4:
					checkBuyBuilding(inputReader, expectedOutputReader);
					break;
				}

			}

			System.out.println(line);
			System.out.println("Total number of test cases: " + testCount);
			System.out.println("Total number of errors: " + errorCount);

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * @param testLine	the test line that is read from the input file
	 * @return			an integer which will correspond to a method from one of the three classes being tested
	 */
	private int matchTestLineToMethodCall(String testLine) {
		int methodNumber = 0;

		if (testLine.equals("test sorting")){
			methodNumber = 1;
		} else if (testLine.equals("selectTileNormal")){
			methodNumber = 2;
		} else if (testLine.equals("selectTileHard")){
			methodNumber = 3;
		} else if (testLine.equals("buyBuilding")){
			methodNumber = 4;
		}
		return methodNumber;
	}

	/**
	 * @param dieValue			specify the die value
	 * @param piecePosition		specify the current position of the game piece
	 * @return					the tiles the player can move to
	 */
	private ArrayList<Integer> generateTilesYouCanMoveTo(int dieValue, int piecePosition) {
		ArrayList<Integer> tiles = new ArrayList<Integer>();

		tiles.add(1 + piecePosition);
		if (dieValue == 1){
			return tiles;	
		}
		tiles.add(2 + piecePosition);
		if (dieValue == 2){
			return tiles;	
		}
		tiles.add(3 + piecePosition);
		if (dieValue == 3){
			return tiles;	
		}
		tiles.add(4 + piecePosition);
		if (dieValue == 4){
			return tiles;	
		}
		tiles.add(5 + piecePosition);
		if (dieValue == 5){
			return tiles;	
		}
		tiles.add(6 + piecePosition);
		if (dieValue == 6){
			return tiles;	
		}
		tiles.add(7 + piecePosition);
		if (dieValue == 7){
			return tiles;	
		}
		tiles.add(8 + piecePosition);
		if (dieValue == 8){
			return tiles;	
		}
		tiles.add(9 + piecePosition);
		if (dieValue == 9){
			return tiles;	
		}
		tiles.add(10 + piecePosition);
		return tiles;
	}

	/**
	 * @param errorMessage	the error message that is read from the input file
	 * @param expected		the expected result that is read from the expected output file
	 * @param result		the result from the testing of the method
	 */
	private void printErrorMessage(String errorMessage,
			String expected, Building result) {
		System.out.println(errorMessage);
		System.out.println("expected: " + expected);
		System.out.println("result: " + result);
		errorCount++;
	}

	/**
	 * @param errorMessage	the error message that is read from the input file
	 * @param expected		the expected result that is read from the expected output file
	 * @param result		the result from the testing of the method
	 */
	private void printErrorMessage(String errorMessage,
			String expected, String result) {
		System.out.println(errorMessage);
		System.out.println("expected: " + expected);
		System.out.println("result: " + result);
		errorCount++;
	}

	/** 
	 * @param errorMessage		the error message this is to be printed if the test case failed
	 * @param expectedResult	the expected result that is supposed to be produce in the test case
	 * @param result			the result from doing the test case
	 */
	private void doTestCase(String errorMessage, String expectedResult, String result) {
		if (!expectedResult.equals(result)){
			printErrorMessage(errorMessage, expectedResult, result);
		}
		testCount++;
	}

	/**
	 * @param errorMessage		the error message this is to be printed if the test case failed
	 * @param building			the result from doing the test case
	 */
	private void doTestCaseResultNull(String errorMessage, Building building) {
		if (building != null){
			printErrorMessage(errorMessage, null, building);
		}
		testCount++;
	}

	/*
	 * the format used to create the following methods which are used for testing:
	 * 1) the error messages from the input file are read
	 * 2) any input data needed for testing are read from the input file
	 * 3) if necessary, those input data that need to be convert to other types are done
	 * 3.1) if necessary, creation of new objects are done
	 * 4) the expected result are read from the expected output file
	 * 4.1) if necessary, update of objects to change settings for testing
	 * 4.2) for each expected result, it is compared to the result from calling the method that is being tested
	 * 5) the 'end of test' message is read from expected output file to indicate that the test case is done 
	 */

	/*
	 * this test case will check the method sortByBuildingPoints from VincentAI.
	 */
	private void checkSorting(BufferedReader inputReader,
			BufferedReader expectedOutputReader) {

		try {

			String errorMessage = inputReader.readLine();

			VincentAI ai = new VincentAI("ai", 0, VincentAI.Mode.NORMAL);

			ArrayList<Building> availableBuilding = new ArrayList<Building>(); 
			BuildingManager manager = new BuildingManager("building.txt");
			Building[] buildingList = manager.getManager();

			for (Building buildingFromManager : buildingList){
				availableBuilding.add(buildingFromManager);
			}

			availableBuilding = ai.sortByBuildingPointsForATD(availableBuilding);

			for (Building building : availableBuilding){
				doTestCase(errorMessage, expectedOutputReader.readLine(), building.getName());
			}

			// end of test
			expectedOutputReader.readLine();

		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	/*
	 * this test case will check the method makeMove from VincentAI for Mode of NORMAL.
	 */
	private void checkSelectTileNormal(BufferedReader inputReader,
			BufferedReader expectedOutputReader) {

		try {
			String errorMessage = inputReader.readLine();

			String stringDieValue = inputReader.readLine();
			String stringDieValue2 = inputReader.readLine();
			String stringDieValue3 = inputReader.readLine();

			int dieValue = Integer.valueOf(stringDieValue);
			int dieValue2 = Integer.valueOf(stringDieValue2);
			int dieValue3 = Integer.valueOf(stringDieValue3);

			VincentAI ai = new VincentAI("ai", 0, VincentAI.Mode.NORMAL);
			gameState.addPlayer(ai);

			ArrayList<Building> allBuildingsAvailableForBuilding = new ArrayList<Building>();
			for (Building b : gameState.getBuildingManager().getManager()){
				allBuildingsAvailableForBuilding.add(b);
			}
			gameState.setAllBuildingsAvailableForBuilding(allBuildingsAvailableForBuilding);

			gameState.setMovementActivityPoints(dieValue);
			gameState.setTilesYouCanMoveTo(generateTilesYouCanMoveTo(dieValue, ai.getPiecePosition()));
			doTestCase(errorMessage + dieValue, expectedOutputReader.readLine(), Integer.toString(ai.selectTile(gameState)));

			gameState.setMovementActivityPoints(dieValue2);			
			gameState.setTilesYouCanMoveTo(generateTilesYouCanMoveTo(dieValue2, ai.getPiecePosition()));
			doTestCase(errorMessage + dieValue2, expectedOutputReader.readLine(), Integer.toString(ai.selectTile(gameState)));

			gameState.setMovementActivityPoints(dieValue3);			
			gameState.setTilesYouCanMoveTo(generateTilesYouCanMoveTo(dieValue3, ai.getPiecePosition()));
			doTestCase(errorMessage + dieValue2, expectedOutputReader.readLine(), Integer.toString(ai.selectTile(gameState)));

			// end of test
			expectedOutputReader.readLine();

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/*
	 * this test case will check the method makeMove from VincentAI for Mode of HARD.
	 */
	private void checkSelectTileHard(BufferedReader inputReader,
			BufferedReader expectedOutputReader) {

		try {
			String errorMessage = inputReader.readLine();

			String stringDieValue = inputReader.readLine();
			String stringDieValue2 = inputReader.readLine();

			int dieValue = Integer.valueOf(stringDieValue);
			int dieValue2 = Integer.valueOf(stringDieValue2);

			VincentAI ai = new VincentAI("ai", 0, VincentAI.Mode.HARD);
			gameState.addPlayer(ai);

			ArrayList<Building> allBuildingsAvailableForBuilding = new ArrayList<Building>();
			for (Building b : gameState.getBuildingManager().getManager()){
				allBuildingsAvailableForBuilding.add(b);
			}
			gameState.setAllBuildingsAvailableForBuilding(allBuildingsAvailableForBuilding);

			gameState.setMovementActivityPoints(dieValue);
			gameState.setTilesYouCanMoveTo(generateTilesYouCanMoveTo(dieValue, ai.getPiecePosition()));
			doTestCase(errorMessage + dieValue, expectedOutputReader.readLine(), Integer.toString(ai.selectTile(gameState)));

			gameState.setMovementActivityPoints(dieValue2);
			gameState.setTilesYouCanMoveTo(generateTilesYouCanMoveTo(dieValue2, ai.getPiecePosition()));
			doTestCase(errorMessage + dieValue2, expectedOutputReader.readLine(), Integer.toString(ai.selectTile(gameState)));

			// to check if the AI will move to the tile with most resources given
			VincentAI ai2 = new VincentAI("ai2", 18, VincentAI.Mode.HARD);
			gameState.addPlayer(ai2);
			gameState.setTilesYouCanMoveTo(generateTilesYouCanMoveTo(dieValue2, ai2.getPiecePosition()));
			doTestCase(errorMessage + dieValue2 + ", for new position 18", expectedOutputReader.readLine(), Integer.toString(ai2.selectTile(gameState)));

			// end of test
			expectedOutputReader.readLine();

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/*
	 * this test case will check the method chooseBuildingToBuy from VincentAI for Mode of NORMAL.
	 */
	private void checkBuyBuilding(BufferedReader inputReader,
			BufferedReader expectedOutputReader) {

		try {
			String errorMessage = inputReader.readLine();
			String errorMessageNoResource = inputReader.readLine();

			VincentAI ai = new VincentAI("ai", 0, VincentAI.Mode.NORMAL);
			gameState.addPlayer(ai);

			ArrayList<Building> allBuildingsAvailableForBuilding = new ArrayList<Building>();
			for (Building b : gameState.getBuildingManager().getManager()){
				allBuildingsAvailableForBuilding.add(b);
			}
			gameState.setAllBuildingsAvailableForBuilding(allBuildingsAvailableForBuilding);

			ai.setCurrentResourceAmount(Player.Resource.MUD, 5);
			ai.setCurrentResourceAmount(Player.Resource.CEMENT, 2);

			doTestCase(errorMessage, expectedOutputReader.readLine(), ai.selectBuilding(gameState).getName());

			ai.setCurrentResourceAmount(Player.Resource.MUD, 10);
			ai.setCurrentResourceAmount(Player.Resource.CEMENT, 10);
			ai.setCurrentResourceAmount(Player.Resource.BRICK, 10);

			doTestCase(errorMessage, expectedOutputReader.readLine(), ai.selectBuilding(gameState).getName());

			ai.setCurrentResourceAmount(Player.Resource.MUD, 0);
			ai.setCurrentResourceAmount(Player.Resource.CEMENT, 0);

			doTestCaseResultNull(errorMessageNoResource, ai.selectBuilding(gameState));

			// end of test
			expectedOutputReader.readLine();

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/*
	 * the input file name: testcasesForVincentAI.txt
	 * the expected output file name: expectedResultsForVincentAI.txt
	 * the data used for Building Manager: testDataForAIBuilding.txt
	 * the data used for Tile Manager: testDataForTile.txt
	 */
	public static void main(String[] args) throws Exception {
		String workingDir = System.getProperty("user.dir");
		String inputFile = workingDir + "\\src\\builders\\testcasesForVincentAI.txt";
		String expectedOutputFile = workingDir + "\\src\\builders\\expectedResultsForVincentAI.txt";
		VincentAIATD atd = new VincentAIATD(inputFile, expectedOutputFile);
		atd.executeTestCases();
	}
}